package com.googlecode.protoserv;

import java.util.HashMap;
import java.util.Vector;

import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.ExtensionRegistry;
import com.googlecode.protoserv.Protocol.RequestProto;
import com.googlecode.protoserv.Protocol.ResponseProto;
import com.googlecode.protoserv.Protocol.RpcStatus;

public class RpcManager {

  private Vector<ServiceSkel> services;
  private HashMap<Integer, ServiceSkel> methodIdToService;
  private ExtensionRegistry registry;

  public RpcManager() {
    this.services = new Vector<ServiceSkel>();
    this.methodIdToService = new HashMap<Integer, ServiceSkel>();
    this.registry = ExtensionRegistry.newInstance();
  }

  public void register(ServiceSkel skeleton) {
    this.services.add(skeleton);
    skeleton.registerExtensions(this.registry);
    int[] methodIds = skeleton.getMethodIds();
    for (int i = 0; i < methodIds.length; i++) {
      this.methodIdToService.put(new Integer(methodIds[i]), skeleton);
    }
  }

  ExtensionRegistry getExtensionRegistry() {
    return this.registry;
  }

  public void processCall(RequestProto request, ResponseProto.Builder response) {
    int methodId = -1;
    ServiceSkel service = null;
    for (FieldDescriptor field : request.getExtensionFields().keySet()) {
      if (field.isExtension()) {
        methodId = field.getNumber();
        service = this.methodIdToService.get(methodId);
        if (service != null) {
          break;
        }
      }
    }
    if (methodId == -1 || service == null) {
      response.setStatus(RpcStatus.RPC_ERROR);
      response.setStatusMessage("Method not found (" + methodId + ").");
      return;
    }
    try {
      RpcHandler rpc = new RpcHandler();
      if (request.hasDeadlineMs()) {
        rpc.setDeadlineMs(request.getDeadlineMs());
      }
      service.run(methodId, rpc, request, response);
      response.setStatus(RpcStatus.SUCCESS);
    } catch (Throwable t) {
      response.clear();
      response.setStatus(RpcStatus.RPC_ERROR);
      response.setStatusMessage(t.getMessage());
    }
  }

}
